﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Collections;

namespace ICeFramework.Win.UI
{
    public partial class PropertyComparerRow : UserControl
    {
        #region Private Properties
        private enum ExpandState
        { 
            Closed,
            Open
        }

        public delegate void RowExpandDelegate(RowExpandEventArgs e);
        public event RowExpandDelegate RowExpand;

        public delegate void WidthChangeDelegate(WidthChangeEventArgs e);
        public event WidthChangeDelegate WidthChange;

        private ExpandState btnState = ExpandState.Closed;
        private string _leftText = string.Empty;
        private string _rightText = string.Empty;

        #endregion

        private string _propertyName;
        #region public string PropertyName
        /// <summary>
        /// Gets the PropertyName of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public string PropertyName
        {
            get { return _propertyName; }
        }
        #endregion

        private string _propertyPath;
        #region public string PropertyPath
        /// <summary>
        /// Gets the PropertyPath of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public string PropertyPath
        {
            get { return _propertyPath; }
        }
        #endregion

        private Type _propertyType;
        #region public Type PropertyType
        /// <summary>
        /// Gets the PropertyType of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public Type PropertyType
        {
            get { return _propertyType; }
        }
        #endregion

        private PropertyComparer _holder;
        #region public PropertyComparer Holder
        /// <summary>
        /// Gets the Holder of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public PropertyComparer Holder
        {
            get { return _holder; }
        }
        #endregion

        private PropertyComparerRow _parentRow;
        #region public PropertyComparerRow ParentRow
        /// <summary>
        /// Gets the ParentRow of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public PropertyComparerRow ParentRow
        {
            get { return _parentRow; }
        }
        #endregion

        private bool _isEqual = true;
        #region public bool IsEqual
        /// <summary>
        /// Gets the IsEqual of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public bool IsEqual
        {
            get { return _isEqual; }
        }
        #endregion

        private int _maxHeight = 21;
        #region public int MaxHeight
        /// <summary>
        /// Get/Sets the MaxHeight of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public int MaxHeight
        {
            get { return _maxHeight; }
            set { _maxHeight = value; }
        }
        #endregion

        private object _leftObject;
        #region public object LeftObject
        /// <summary>
        /// Gets the LeftObject of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public object LeftObject
        {
            get { return _leftObject; }
        }
        #endregion

        private object _rightObject;
        #region public object RightObject
        /// <summary>
        /// Gets the RightObject of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public object RightObject
        {
            get { return _rightObject; }
        }
        #endregion

        private string _leftPropertyValue = string.Empty;
        #region public string LeftPropertyValue
        /// <summary>
        /// Get/Sets the LeftPropertyValue of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public string LeftPropertyValue
        {
            get { return _leftPropertyValue; }
            set { _leftPropertyValue = value; }
        }
        #endregion

        private string _rightPropertyValue = string.Empty;
        #region public string RightPropertyValue
        /// <summary>
        /// Get/Sets the RightPropertyValue of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public string RightPropertyValue
        {
            get { return _rightPropertyValue; }
            set { _rightPropertyValue = value; }
        }
        #endregion

        private bool _isReadOnly;
        #region public bool IsReadOnly
        public bool IsReadOnly
        {
            get { return _isReadOnly; }
            set { _isReadOnly = value; }
        }
        #endregion

        private bool _hasProperties = false;
        #region public bool HasProperties
        /// <summary>
        /// Get/Sets the HasProperties of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public bool HasProperties
        {
            get { return _hasProperties; }
            set { _hasProperties = value; }
        }
        #endregion

        private int _level = 0;
        #region public int Level
        /// <summary>
        /// Gets the Level of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public int Level
        {
            get { return _level; }
        }
        #endregion

        private int _index = 0;
        #region public int Index
        /// <summary>
        /// Gets the Index of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public int Index
        {
            get { return _index; }
        }
        #endregion

        private int _levelCount = 0;
        #region public int LevelCount
        /// <summary>
        /// Gets the LevelCount of the PropertyComparerRow
        /// </summary>
        /// <value></value>
        public int LevelCount
        {
            get { return _levelCount; }
        }
        #endregion

        #region public PropertyComparerRow(PropertyComparer holder, PropertyComparerRow parentRow, bool isReadOnly, string propertyPath, string propertyName, object leftObject, object rightObject)
        /// <summary>
        /// Initializes a new instance of the <b>PropertyComparerRow</b> class.
        /// </summary>
        /// <param name="holder"></param>
        /// <param name="parentRow"></param>
        /// <param name="isReadOnly"></param>
        /// <param name="width"></param>
        /// <param name="propertyPath"></param>
        /// <param name="propertyName"></param>
        /// <param name="leftObject"></param>
        /// <param name="rightObject"></param>
        public PropertyComparerRow(PropertyComparer holder, PropertyComparerRow parentRow, bool isReadOnly, string propertyPath, string propertyName, object leftObject, object rightObject)
        {
            InitializeComponent();

            _parentRow = parentRow;
            if (_parentRow == null)
            {
                _index = holder.Controls.Count;
            }
            else
            {
                _index = _parentRow.pnlProperties.Controls.Count;
                _level = _parentRow.Level + 1;
                _parentRow.WidthChange += new WidthChangeDelegate(_parentRow_WidthChange);
            }
            _holder = holder;
            _levelCount = _level;

            //int propertyWidth = (int)_holder.RowWidth / 2 - _level * _holder.Indent;
            //if (propertyWidth < _holder.MinPropertyWidth)
            //{
            //    _holder.RowWidth += _holder.MinPropertyWidth - propertyWidth;
            //}

            this.Height = _maxHeight;
            int recommendedWidth = 2 * (_holder.MinPropertyWidth + _level * _holder.Indent);
            if (this.Width < recommendedWidth)
            {
                this.Width = recommendedWidth;
            }
            //this.Width = 1000;
            //_holder.Width = _holder.RowWidth;

            _leftObject = leftObject;
            _rightObject = rightObject;
            _propertyName = propertyName;
            _propertyPath = propertyPath;
            _isReadOnly = isReadOnly;

            SetState(ExpandState.Closed);
            pnlProperties.Top = _holder.RowHeight;
            pnlProperties.Left = 0;
            //pnlProperties.Width = _holder.RowWidth;

            btnExpandLeft.BackColor = this.BackColor;
            btnExpandLeft.Left = _level * _holder.Indent;
            btnExpandLeft.Top = 0;
            btnExpandLeft.Height = _holder.RowHeight;

            btnExpandRight.BackColor = this.BackColor;
            
            btnExpandRight.Top = 0;
            btnExpandRight.Height = _holder.RowHeight;

            bool hasLeftProps;
            bool hasRightProps;
            _leftPropertyValue = GetValue(_leftObject, out hasLeftProps);
            _rightPropertyValue = GetValue(_rightObject, out hasRightProps);

            if (_leftObject != null)
            {
                if (!string.IsNullOrEmpty(_propertyName) && !string.IsNullOrEmpty(_leftPropertyValue))
                {
                    _leftText = string.Format("{0} ({1})", _propertyName, _leftPropertyValue);
                }
                else if (!string.IsNullOrEmpty(_leftPropertyValue))
                {
                    _leftText = _leftPropertyValue;
                }
                else
                {
                    _leftText = _propertyName;
                }
            }

            if (_rightObject != null)
            {
                if (!string.IsNullOrEmpty(_propertyName) && !string.IsNullOrEmpty(_rightPropertyValue))
                {
                    _rightText = string.Format("{0} ({1})", _propertyName, _rightPropertyValue);
                }
                else if (!string.IsNullOrEmpty(_rightPropertyValue))
                {
                    _rightText = _rightPropertyValue;
                }
                else
                {
                    _rightText = _propertyName;
                }
            }

            SetComparition(_leftText == _rightText);

            object referenceObject = leftObject == null ? rightObject : leftObject;
            _propertyType = referenceObject.GetType();
            if (referenceObject != null)
            {
                if ((hasLeftProps || hasRightProps) && !_holder.References.ContainsValue(referenceObject.GetHashCode()))
                {
                    int nextId = _holder.References.Count + 1;
                    _holder.References.Add(nextId, referenceObject.GetHashCode());
                    _hasProperties = GetProperties() > 0;
                    _isReadOnly = true;
                }
                else
                {
                    _hasProperties = false;
                }
            }
            btnExpandLeft.Visible = leftObject != null && _hasProperties;
            btnExpandRight.Visible = rightObject != null && _hasProperties;
            
            if (_isEqual)
            {
                toolTip1.SetToolTip(this, string.Format("{0} = {1}", _leftText, _rightText));
            }
            else
            {
                toolTip1.SetToolTip(this, string.Format("{0} != {1}", _leftText, _rightText));
            }
        }
        #endregion

        void _parentRow_WidthChange(WidthChangeEventArgs e)
        {
            SetWidth(this.Width + e.GrowFactor);
        }

        #region private void SetComparition(bool childEquality)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="childEquality"></param>
        private void SetComparition(bool childEquality)
        {
            if (_isEqual && !childEquality)
            {
                _isEqual = false;
            }
        }
        #endregion

        #region private string GetValue(Type type, object data)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="property"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private string GetValue(object data, out bool hasProperties)
        {
            bool hasProps = false;
            string sRet = string.Empty;
            if (data != null)
            {
                Type type = data.GetType();
                switch (type.Name.ToLower())
                {
                    case "int":
                    case "int16":
                    case "int32":
                    case "int64":
                    case "uint":
                    case "uint16":
                    case "uint32":
                    case "uint64":
                    case "double":
                    case "float":
                    case "single":
                    case "string":
                    case "guid":
                        sRet = data.ToString();
                        break;
                    default:
                        if (type.IsClass)
                        {
                            hasProps = true;
                            sRet = data.ToString();
                            if (data is IList || data is ICollection)
                            {
                                sRet = string.Empty;
                            }
                        }
                        else
                        {
                            hasProps = false;
                            sRet = data.ToString();
                            if (data is IList || data is ICollection)
                            {
                                sRet = string.Empty;
                            }
                        }
                        break;
                }
            }
            hasProperties = hasProps;
            return sRet;
        }
        #endregion

        #region private void GetProperties()
        /// <summary>
        /// 
        /// </summary>
        private int GetProperties()
        {
            int iRet = 0;
            object referenceObject = _leftObject == null ? _rightObject : _leftObject;

            int minWidth = this.Width;
            if (referenceObject is IList)
            {
                _isReadOnly = true;
                IList lft = _leftObject as IList;
                IList rght = _rightObject as IList;

                int count = lft != null && rght != null ? (lft.Count > rght.Count ? lft.Count : rght.Count) : (lft == null ? rght.Count : lft.Count);
                
                for (int i = 0; i < count; i++)
                {
                    object valLeft = lft != null && lft.Count > i ? lft[i] : null;
                    object valRight = rght != null && rght.Count > i ? rght[i] : null;

                    PropertyComparerRow row = new PropertyComparerRow(_holder, this, false, string.Format("{0}.List[{1}]", _propertyPath, i), string.Empty, valLeft, valRight);
                    if (_levelCount < row.LevelCount)
                    {
                        _levelCount = row.LevelCount;
                    }
                    if (minWidth < row.Width)
                    {
                        SetWidth(row.Width);
                        if (WidthChange != null)
                        {
                            WidthChange(new WidthChangeEventArgs(row.Width - minWidth));
                        }
                        minWidth = row.Width;
                    }


                    //pnlProperties.Width = 1000;
                    pnlProperties.Height += _holder.RowHeight;
                    //row.Width = 1000;

                    pnlProperties.Controls.Add(row);
                    _maxHeight += _holder.RowHeight;
                    row.RowExpand += new RowExpandDelegate(row_RowExpand);
                    row.WidthChange += new WidthChangeDelegate(row_WidthChange);
                    SetComparition(row.IsEqual);
                    iRet++;
                }
            }
            else if (referenceObject is ICollection)
            {
                _isReadOnly = true;
                ICollection lft = _leftObject as ICollection;
                ICollection rght = _rightObject as ICollection;
                object[] lftObj = new object[lft.Count];
                object[] rghtObj = new object[rght.Count];
                lft.CopyTo(lftObj, 0);
                rght.CopyTo(rghtObj, 0);
                for (int i = 0; i < lft.Count; i++)
                {
                    PropertyComparerRow row = new PropertyComparerRow(_holder, this, false, string.Format("{0}.List[{1}]", _propertyPath, i), string.Empty, lftObj[i], rghtObj[i]);
                    if (_levelCount < row.LevelCount)
                    {
                        _levelCount = row.LevelCount;
                    }
                    if (minWidth < row.Width)
                    {
                        SetWidth(row.Width);
                        if (WidthChange != null)
                        {
                            WidthChange(new WidthChangeEventArgs(row.Width - minWidth));
                        }
                        minWidth = row.Width;
                    }
                    //pnlProperties.Width = 1000;
                    pnlProperties.Height += _holder.RowHeight;
                    //row.Width = 1000;
                    pnlProperties.Controls.Add(row);
                    _maxHeight += _holder.RowHeight;
                    row.RowExpand += new RowExpandDelegate(row_RowExpand);
                    row.WidthChange += new WidthChangeDelegate(row_WidthChange);
                    SetComparition(row.IsEqual);
                    iRet++;
                }
            }
            else
            {
                PropertyInfo[] props = referenceObject.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
                foreach (PropertyInfo property in props)
                {
                    object valLeft = _leftObject != null ? property.GetValue(_leftObject, null) : null;
                    object valRight = _rightObject != null ? property.GetValue(_rightObject, null) : null;
                    if (valLeft != null || valRight != null)
                    {
                        PropertyComparerRow row = new PropertyComparerRow(_holder, this, !property.CanWrite, string.Format("{0}.{1}", _propertyPath, property.Name), property.Name, valLeft, valRight);
                        if (_levelCount < row.LevelCount)
                        {
                            _levelCount = row.LevelCount;
                        }
                        if (minWidth < row.Width)
                        {
                            SetWidth(row.Width);
                            if (WidthChange != null)
                            {
                                WidthChange(new WidthChangeEventArgs(row.Width - minWidth));
                            }
                            minWidth = row.Width;
                        }
                        //pnlProperties.Width = 1000;
                        pnlProperties.Height += _holder.RowHeight;
                        //row.Width = 1000;
                        this.pnlProperties.Controls.Add(row);
                        _maxHeight += _holder.RowHeight;
                        row.RowExpand += new RowExpandDelegate(row_RowExpand);
                        row.WidthChange += new WidthChangeDelegate(row_WidthChange);
                        SetComparition(row.IsEqual);
                        iRet++;
                    }
                }
            }
            return iRet;
        }

        void row_WidthChange(WidthChangeEventArgs e)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region void row_RowExpand(RowExpandEventArgs e)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        void row_RowExpand(RowExpandEventArgs e)
        {
            int height = this.Height;
            this.Height += e.ExpandFactor;
            pnlProperties.Height += e.ExpandFactor;
            pnlProperties.Top = _holder.RowHeight;
            if (RowExpand != null)
            {
                RowExpand(new RowExpandEventArgs(this.Height - height));
            }
        }
        #endregion

        #region private void btnExpand_Click(object sender, EventArgs e)
        /// <summary>
        /// This method is called when the btnExpand's Click event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> of the event.</param>
        private void btnExpand_Click(object sender, EventArgs e)
        {
            switch (btnState)
            {
                case ExpandState.Closed:
                    SetState(ExpandState.Open);
                    break;
                case ExpandState.Open:
                    SetState(ExpandState.Closed);
                    break;
            }
        }
        #endregion

        #region private void SetState(ExpandState state)
        /// <summary>
        /// 
        /// </summary>
        /// <param name="state"></param>
        private void SetState(ExpandState state)
        {
            int height;
            switch (state)
            {
                case ExpandState.Closed:
                    height = this.Height;
                    this.Height = _holder.RowHeight;
                    pnlProperties.Top = _holder.RowHeight;
                    btnExpandLeft.ImageIndex = 0;
                    btnExpandRight.ImageIndex = 0;
                    btnState = ExpandState.Closed;
                    if (RowExpand != null)
                    {
                        RowExpand(new RowExpandEventArgs(this.Height - height));
                    }
                    break;
                case ExpandState.Open:
                    height = this.Height;
                    pnlProperties.Height = 0;
                    foreach (Control child in this.pnlProperties.Controls)
                    {
                        pnlProperties.Height += child.Height;
                    }
                    this.Height = pnlProperties.Height + _holder.RowHeight;
                    pnlProperties.Top = _holder.RowHeight;
                    if (RowExpand != null)
                    {
                        RowExpand(new RowExpandEventArgs(this.Height - height));
                    }
                    btnExpandLeft.ImageIndex = 1;
                    btnExpandRight.ImageIndex = 1;
                    btnState = ExpandState.Open;
                    break;
            }
        }
        #endregion

        private void SetWidth(int width)
        {
            this.Width = width;
            this.pnlProperties.Width = width;
            btnExpandRight.Left = this.Width - btnExpandRight.Width - _level * _holder.Indent - 3;
        }

        #region private void PropertyComparerRow_Paint(object sender, PaintEventArgs e)
        /// <summary>
        /// This method is called when the PropertyComparerRow's Paint event has been fired.
        /// </summary>
        /// <param name="sender">The <see cref="object"/> that fired the event.</param>
        /// <param name="e">The <see cref="PaintEventArgs"/> of the event.</param>
        private void PropertyComparerRow_Paint(object sender, PaintEventArgs e)
        {
            if (_parentRow != null && _parentRow.PropertyType != null)
            {
                bool excluded = false;
                foreach (KeyValuePair<Type, string> de in _holder.ExcludedProperties)
                {
                    if (de.Key.FullName == _parentRow.PropertyType.FullName && de.Value == _propertyName)
                    {
                        excluded = true;
                        break;
                    }
                }

                if (excluded)
                {
                    e.Graphics.FillRectangle(Brushes.Aqua, this.ClientRectangle);
                }
            }


            string leftText = string.Empty;
            string rightText = string.Empty;
            string equalSign = string.Empty;
            Font drawFont;
            Brush drawBrush;
            Brush equalBrush;
            //SizeF leftTextSize;
            RectangleF leftTextCoord = new RectangleF();
            //SizeF rightTextSize;
            RectangleF rightTextCoord = new RectangleF();
            SizeF equalTextSize;
            RectangleF equalTextCoord = new RectangleF();

            //SizeF propertyNameTextSize;

            int leftOffset = 0;
            int rightOffset = 0;
            int middleWidth = 20;

            e.Graphics.FillRectangle(Brushes.White, this.ClientRectangle);

            if (_hasProperties)
            {
                drawFont = new Font(this.Font, FontStyle.Bold);
                //leftTextSize = e.Graphics.MeasureString(_leftText, drawFont, 1000);
                //rightTextSize = e.Graphics.MeasureString(_rightText, drawFont, 1000);
                //propertyNameTextSize = e.Graphics.MeasureString(_propertyName, drawFont);
                if (!string.IsNullOrEmpty(_leftText))
                {
                    leftOffset = 5 + btnExpandLeft.Width;
                }
                if (!string.IsNullOrEmpty(_rightText))
                {
                    rightOffset = 5 + btnExpandRight.Width;
                }

                if (_isReadOnly)
                {
                    drawBrush = new SolidBrush(Color.Gray);
                }
                else
                {
                    drawBrush = new SolidBrush(Color.Black);
                }
            }
            else
            {
                drawFont = new Font(this.Font, FontStyle.Regular);
                //leftTextSize = e.Graphics.MeasureString(_leftText, drawFont);
                //rightTextSize = e.Graphics.MeasureString(_rightText, drawFont);
                
                //propertyNameTextSize = e.Graphics.MeasureString(_propertyName, drawFont);

                if (!string.IsNullOrEmpty(_leftText))
                {
                    leftOffset = 5;
                }
                if (!string.IsNullOrEmpty(_rightText))
                {
                    rightOffset = 5;
                }
                if (_isReadOnly)
                {
                    drawBrush = new SolidBrush(Color.Gray);
                }
                else
                {
                    drawBrush = new SolidBrush(Color.Black);
                }
            }
            if (_isEqual)
            {
                equalSign = "=";
                equalTextSize = e.Graphics.MeasureString(equalSign, this.Font);
                equalBrush = new SolidBrush(Color.Black);
            }
            else
            {
                equalSign = "!=";
                equalTextSize = e.Graphics.MeasureString(equalSign, this.Font);
                equalBrush = new SolidBrush(Color.Red);
            }

            //float graterOffset = (propertyNameTextSize.Width + 100) - (_holder.RowWidth / 2 - _holder.Indent * _level - leftOffset - middleWidth / 2);
            //if (graterOffset > 0)
            //{
            //    _holder.RowWidth += (int)graterOffset;
            //    _holder.Invalidate();
            //    //return;
            //}

            //leftTextCoord = new RectangleF(new PointF(_holder.Indent * _level + leftOffset, 0), new SizeF(_holder.RowWidth / 2 - _holder.Indent * _level - leftOffset - middleWidth / 2, _holder.RowHeight));
            //rightTextCoord = new RectangleF(new PointF(_holder.RowWidth / 2 + middleWidth / 2, 0), new SizeF(_holder.RowWidth / 2 - _holder.Indent * _level - rightOffset - middleWidth / 2, _holder.RowHeight));
            //equalTextCoord = new RectangleF(new PointF(_holder.RowWidth / 2 - middleWidth / 2, 0), new SizeF(middleWidth, _holder.RowHeight));
            leftTextCoord = new RectangleF(new PointF(_holder.Indent * _level + leftOffset, 0), new SizeF(this.Width / 2 - _holder.Indent * _level - leftOffset - middleWidth / 2, _holder.RowHeight));
            rightTextCoord = new RectangleF(new PointF(this.Width / 2 + middleWidth / 2, 0), new SizeF(this.Width / 2 - _holder.Indent * _level - rightOffset - middleWidth / 2, _holder.RowHeight));
            equalTextCoord = new RectangleF(new PointF(this.Width / 2 - middleWidth / 2, 0), new SizeF(middleWidth, _holder.RowHeight));


            if (!string.IsNullOrEmpty(_leftText))
            {
                leftText = string.Format("{0} - ({1})", _leftText, leftTextCoord.Width);
                e.Graphics.DrawRectangle(Pens.Green, leftTextCoord.X, leftTextCoord.Y, leftTextCoord.Width, leftTextCoord.Height);
                using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap))
                {
                    sf.Trimming = StringTrimming.EllipsisCharacter;
                    sf.Alignment = StringAlignment.Near;
                    sf.LineAlignment = StringAlignment.Center;
                    e.Graphics.DrawString(leftText, drawFont, drawBrush, leftTextCoord, sf);
                }
            }
            if (!string.IsNullOrEmpty(_rightText))
            {
                rightText = string.Format("{0} - ({1})", _rightText, rightTextCoord.Width);
                e.Graphics.DrawRectangle(Pens.Green, rightTextCoord.X, rightTextCoord.Y, rightTextCoord.Width, rightTextCoord.Height);
                using (StringFormat sf = new StringFormat(StringFormatFlags.NoWrap))
                {
                    sf.Trimming = StringTrimming.EllipsisCharacter;
                    sf.Alignment = StringAlignment.Far;
                    sf.LineAlignment = StringAlignment.Center;
                    e.Graphics.DrawString(rightText, drawFont, drawBrush, rightTextCoord, sf);
                }
            }

            //e.Graphics.DrawRectangle(Pens.Blue, equalTextCoord.X, equalTextCoord.Y, equalTextCoord.Width, equalTextCoord.Height);
            using (StringFormat sf = new StringFormat())
            {
                sf.LineAlignment = StringAlignment.Center;
                sf.Alignment = StringAlignment.Center;
                e.Graphics.DrawString(equalSign, this.Font, equalBrush, equalTextCoord, sf);
            }

            if (_parentRow != null)
            {
                int xOffset = (int)btnExpandLeft.Width / 2;
                if (_index < _parentRow.pnlProperties.Controls.Count - 1)
                {
                    e.Graphics.DrawLine(Pens.Gray, new Point((_level - 1) * _holder.Indent + xOffset, 0), new Point((_level - 1) * _holder.Indent + xOffset, (int)_holder.RowHeight));
                    e.Graphics.DrawLine(Pens.Gray, new Point((this.Width - ((_level - 1) * _holder.Indent)) - xOffset - 3, 0), new Point((this.Width - ((_level - 1) * _holder.Indent)) - xOffset - 3, (int)_holder.RowHeight));
                }
                else
                {
                    e.Graphics.DrawLine(Pens.Gray, new Point((_level - 1) * _holder.Indent + xOffset, 0), new Point((_level - 1) * _holder.Indent + xOffset, (int)_holder.RowHeight / 2));
                    e.Graphics.DrawLine(Pens.Gray, new Point((this.Width - ((_level - 1) * _holder.Indent)) - xOffset - 3, 0), new Point((this.Width - ((_level - 1) * _holder.Indent)) - xOffset - 3, (int)_holder.RowHeight / 2));
                }

                PropertyComparerRow row = _parentRow;
                while (row != null)
                {
                    bool canDraw = row.ParentRow != null && row.Index < row.ParentRow.pnlProperties.Controls.Count - 1;
                    if (canDraw)
                    {
                        e.Graphics.DrawLine(Pens.Gray, new Point(row.ParentRow.Level * _holder.Indent + xOffset, 0), new Point(row.ParentRow.Level * _holder.Indent + xOffset, (int)_holder.RowHeight));
                        e.Graphics.DrawLine(Pens.Gray, new Point(this.Width - (row.ParentRow.Level * _holder.Indent + xOffset) - 3, 0), new Point(this.Width - (row.ParentRow.Level * _holder.Indent + xOffset) - 3, (int)_holder.RowHeight));
                    }
                    row = row.ParentRow;
                }

                e.Graphics.DrawLine(Pens.Gray, new Point((_level - 1) * _holder.Indent + xOffset, (int)_holder.RowHeight / 2), new Point(btnExpandLeft.Left - 2, (int)_holder.RowHeight / 2));
                e.Graphics.DrawLine(Pens.Gray, new Point((this.Width - ((_level - 1) * _holder.Indent)) - xOffset - 3, (int)_holder.RowHeight / 2), new Point(btnExpandRight.Left + btnExpandRight.Width + 2, (int)_holder.RowHeight / 2));
            }
        }
        #endregion

        //private void pnlProperties_Resize(object sender, EventArgs e)
        //{
        //    foreach (PropertyComparerRow row in pnlProperties.Controls)
        //    {
        //        row.Width = pnlProperties.Width;
        //        row.btnExpandLeft.Left = row.Level * _holder.Indent;
        //        row.btnExpandRight.Left = this.Width - row.btnExpandRight.Width - row.Level * _holder.Indent - 3;
        //    }
        //}
    }

    #region public class RowExpandEventArgs
    /// <summary>
    /// 
    /// </summary>
    /// <remarks></remarks>
    /// <example></example>
    public class RowExpandEventArgs
    {
        private int _expandFactor;
        #region public int ExpandFactor
        /// <summary>
        /// Get/Sets the ExpandFactor of the RowExpandEventArgs
        /// </summary>
        /// <value></value>
        public int ExpandFactor
        {
            get { return _expandFactor; }
            set { _expandFactor = value; }
        }
        #endregion

        #region public RowExpandEventArgs(int expandFactor)
        /// <summary>
        /// Initializes a new instance of the <b>RowExpandEventArgs</b> class.
        /// </summary>
        /// <param name="expandFactor"></param>
        public RowExpandEventArgs(int expandFactor)
        {
            _expandFactor = expandFactor;
        }
        #endregion
    }
    #endregion

    #region public class WidthChangeEventArgs
    /// <summary>
    /// 
    /// </summary>
    /// <remarks></remarks>
    /// <example></example>
    public class WidthChangeEventArgs
    {
        private int _growFactor;
        #region public int GrowFactor
        /// <summary>
        /// Get/Sets the GrowFactor of the RowResizeEventArgs
        /// </summary>
        /// <value></value>
        public int GrowFactor
        {
            get { return _growFactor; }
            set { _growFactor = value; }
        }
        #endregion

        #region public WidthChangeEventArgs(int growFactor)
        /// <summary>
        /// Initializes a new instance of the <b>WidthChangeEventArgs</b> class.
        /// </summary>
        /// <param name="growFactor"></param>
        public WidthChangeEventArgs(int growFactor)
        {
            _growFactor = growFactor;
        }
        #endregion
    }
    #endregion
}
